// AStr.cpp

#include "stdafx.h"
#include "AStr.h"
#pragma warning(disable:4996)
#pragma warning(disable:4995)
#pragma warning(disable:4244)
#pragma comment(lib,"strsafe.lib")

namespace
{

#pragma pack(push,1)
	class AStrPrivateData
	{
	public:
		long	m_iRefCount;
		//	int32	iRefCount;
		int32	m_iCapacity;
		int32	m_iLen;
		char	m_sTxt[4];

		void Release()
		{
			if(::InterlockedDecrement(&m_iRefCount)==0){Free(this);};
		};
		void IncRef()
		{
			InterlockedIncrement(&m_iRefCount);
		};

		static AStrPrivateData* Allocate(int p_iCapacity)
		{
			int iAddSize=((p_iCapacity-4)+15)&(~0xf);
			iAddSize=Max(0,iAddSize);
			int iMemSize=sizeof(AStrPrivateData)+iAddSize;
			AStrPrivateData* pxD=static_cast<AStrPrivateData*>(malloc(iMemSize));
			pxD->m_iLen=0;
			pxD->m_iCapacity=iAddSize+4;
			pxD->m_iRefCount=0;
			pxD->m_sTxt[0]=0;
			return pxD; 
		};

		static void Free(AStrPrivateData* p_pxD)
		{
			free(p_pxD);
		};

		static AStrPrivateData* Realloc(AStrPrivateData* p_pxD,int p_iCapacity)
		{
			if(!p_pxD)
			{
				return Allocate(p_iCapacity);
			};
			int iAddSize=((p_iCapacity-4)+15)&(~0xf);
			if(iAddSize==p_pxD->m_iCapacity-4)
			{
				return p_pxD;
			};
			int iMemSize=sizeof(AStrPrivateData)+iAddSize;
			AStrPrivateData* pxD=static_cast<AStrPrivateData*>(realloc(p_pxD,iMemSize));
			pxD->m_iCapacity=iAddSize+4;
			return pxD;
		};

		static AStrPrivateData* Char2Data(const char* m_sText)
		{
			const char* sMem=m_sText;
			sMem-=12;
			return reinterpret_cast<AStrPrivateData*>(const_cast<char*>(sMem));
		};
		static char* Data2Char(AStrPrivateData* p_pxS)
		{
			return &(p_pxS->m_sTxt[0]);
		};
	};
#pragma pack(pop,1)

}; // anon namespace

/** \ingroup PTBase_DataTypes
\class AStr
\brief ANSI String

AStr is a string of simple chars, usually used for pure ANSI text.
*/


/// DTor, frees memory if this AStr holds the last reference
AStr::~AStr()
{
	if(m_sData)
	{
		AStrPrivateData::Char2Data(m_sData)->Release();
	};
};

/// CTor, initialize to NULL
AStr::AStr()
{
	m_sData=NULL;
};

/// Copy-CTor, make a new reference to the string in \p sA
/// \param p_sA string to copy
AStr::AStr(const AStr& p_sA)
{
	m_sData=p_sA.m_sData;
	if(m_sData){AStrPrivateData::Char2Data(m_sData)->IncRef();};
};

/// Assigment, frees the referenced text and makes a new reference to the given string.
AStr& AStr::operator=(const AStr& p_sA)
{
	if(&(p_sA)==this){return *this;};
	char* sOld=m_sData;
	m_sData=p_sA.m_sData;
	if(m_sData){AStrPrivateData::Char2Data(m_sData)->IncRef();};
	if(sOld)
	{
		AStrPrivateData::Char2Data(sOld)->Release();
	};
	return *this;
};

/// Build a single-char string
AStr::AStr(const char p_kcC)
{
	AStrPrivateData* pxD=AStrPrivateData::Allocate(2);
	pxD->m_iLen=1;
	pxD->m_sTxt[0]=p_kcC;
	pxD->m_sTxt[1]=0;
	pxD->IncRef();
	m_sData=AStrPrivateData::Data2Char(pxD);
};

/** \brief Builds a new AStr from \p sText which may be NULL.
\param p_sText char pointer to copy
*/
AStr::AStr(const char* p_sText)
{
	if((!p_sText)||((*p_sText)=='\0'))
	{
		m_sData=NULL;
	}
	else
	{
		int iL=(int)(::lstrlenA(p_sText));
		AStrPrivateData* pxD=AStrPrivateData::Allocate(iL+1);
		pxD->m_iLen=iL;
		memcpy(pxD->m_sTxt,p_sText,iL+1);
		pxD->IncRef();
		m_sData=AStrPrivateData::Data2Char(pxD);
	};
};

/** \brief sets AStr from \p sText which may be NULL.
\param p_sText char pointer to copy

The old referenced text - if not NULL - will be released by the last user.
*/
AStr& AStr::operator=(const char* p_sText)
{
	if(p_sText==m_sData){return *this;};
	char* sOld=m_sData;
	if((!p_sText)||((*p_sText)=='\0'))
	{
		m_sData=NULL;
	}
	else
	{
		int iL=(int)(::lstrlenA(p_sText));
		AStrPrivateData* pxD=AStrPrivateData::Allocate(iL+1);
		memcpy(pxD->m_sTxt,p_sText,iL+1);
		pxD->m_iLen=iL;
		pxD->IncRef();
		m_sData=AStrPrivateData::Data2Char(pxD);
	};
	if(sOld)
	{
		AStrPrivateData::Char2Data(sOld)->Release();
	};
	return *this;
};

void AStr::InternalAddRef(char* p_sV)
{
	if(p_sV)
	{
		AStrPrivateData::Char2Data(p_sV)->IncRef();
	};
};

void AStr::InternalRelease(char* p_sV)
{
	if(p_sV)
	{
		AStrPrivateData::Char2Data(p_sV)->Release();
	};
};


/** \brief implicit const char* conversion

If the encapsulated char pointer is NULL, an empty string will
be returned instead of the NULL pointer.
*/
AStr::operator const char* () const
{
	if(m_sData)
	{
		return m_sData;
	}
	else
	{
		return "";
	};
};

/** \brief explicit const char* conversion

if the encapsulated char pointer is NULL, an empty string will
be returned instead of the NULL pointer.
*/
const char* AStr::c_str() const
{
	if(m_sData)
	{
		return m_sData;
	}
	else
	{
		return "";
	};
};

/** \brief Make this string unique removing all other references
*/
void AStr::Isolate()
{
	if(!m_sData){return;};
	AStrPrivateData* pxD=AStrPrivateData::Char2Data(m_sData);
	if(pxD->m_iRefCount<=1){return;};
	GetBuffer(pxD->m_iCapacity);
};

/** \brief Make this string a unique buffer of size \p iMinSize
\param p_iMinSize minimal size of new buffer

The returned pointer is safe to use for all other APIs that
require a char* pointer.

Calling GetBuffer on an empty (NULL) string with iMinSize=0
returns a NULL pointer.
*/
char* AStr::GetBuffer(int p_iMinSize)
{
	{
		int iL=GetSize();
		p_iMinSize=max(iL+1,p_iMinSize);
	};
	AStrPrivateData* pxD;
	if(!m_sData)
	{
		if(p_iMinSize==0)
		{
			return m_sData;
		};
		pxD=AStrPrivateData::Allocate(p_iMinSize);
		pxD->IncRef();
		pxD->m_iLen=-1;
		m_sData=AStrPrivateData::Data2Char(pxD);
		return m_sData;
	};
	pxD=AStrPrivateData::Char2Data(m_sData);
	if(pxD->m_iRefCount<=1)
	{
		if(pxD->m_iCapacity>=p_iMinSize){
			pxD->m_iLen=-1;
			return m_sData;
		};
		AStrPrivateData* pxNew=AStrPrivateData::Realloc(pxD,p_iMinSize);
		m_sData=AStrPrivateData::Data2Char(pxNew);
		pxNew->m_iLen=-1;
		return m_sData;
	}
	else
	{
		AStrPrivateData* pxNew=AStrPrivateData::Allocate(p_iMinSize);
		int iCopy=Min(pxD->m_iLen+1,p_iMinSize);
		if(iCopy>0)
		{
			memcpy(pxNew->m_sTxt,pxD->m_sTxt,iCopy);
		};
		pxNew->IncRef();
		pxD->Release();
		pxNew->m_iLen=-1;
		m_sData=AStrPrivateData::Data2Char(pxNew);
		return m_sData;
	};
};

char AStr::GetAt(int p_iIndex) const
{
	if(!m_sData){return '\0';};
	if(p_iIndex>=GetSize()){return '\0';};
	return m_sData[p_iIndex];
};

void AStr::SetAt(int p_iIndex,char p_cChar)
{
	GetBuffer(p_iIndex+1)[p_iIndex]=p_cChar;
	AStrPrivateData::Char2Data(m_sData)->m_iLen=-1;
};

/** \brief Calculate length of string \p sS in bytes
\param sS null-terminated char string

This static method lessens the need to include stdlib headers.
*/
int AStr::StrLen(const char* p_sS)
{
	return ::lstrlenA(p_sS);
};

/** \brief Get length of string in bytes
*/
int AStr::GetSize() const
{
	if(!m_sData){return 0;};
	AStrPrivateData* pxD=AStrPrivateData::Char2Data(m_sData);
	if(pxD->m_iLen!=-1){return pxD->m_iLen;};
	pxD->m_iLen=::lstrlenA(pxD->m_sTxt);
	return pxD->m_iLen;
};

/** \brief Get current allocated buffer size
*/
int AStr::GetInternalCapacity() const
{
	if(!m_sData){return 0;};
	AStrPrivateData* pxD=AStrPrivateData::Char2Data(m_sData);
	return pxD->m_iCapacity;
};

/** \brief Reset this string to a null pointer

If there are other AStr references to the managed string, they
will remain valid.
*/
void AStr::Clear()
{
	if(!m_sData){return;};
	AStrPrivateData::Char2Data(m_sData)->Release();
	m_sData=NULL;
};

/** \brief Test two AStr instances for equality
\param sA second string to test against    
*/
bool AStr::IsEqual(const AStr& p_sA) const
{
	if(m_sData==p_sA.m_sData){return true;};
	int iL1=GetSize();
	int iL2=p_sA.GetSize();
	if(iL1!=iL2){return false;};
	if(iL1==0){return true;};
	const char* sC=m_sData;
	const char* sB=p_sA.m_sData;
	while((--iL1)>=0)
	{
		if((*(sC))!=(*(sB))){return false;};sC++;sB++;
	};
	return true;
};

bool AStr::IsEqual(const char* p_sA) const
{
	const char* sB=m_sData;
	if(!sB)
	{
		if((!p_sA)||(*p_sA=='\0')){return true;}else{return false;};
	};
	if(!p_sA){return false;};
	while((*p_sA==*sB)&&(*p_sA!='\0')&&(*sB!='\0'))
	{
		p_sA++;sB++;
	};
	return((*p_sA=='\0')&&(*sB=='\0'));
};

/** \brief printf-like string formatting
\param sFormat format string

The total length of the result is limited to 1024 bytes.
*/
void AStr::Format(const char* p_sFormat,...)
{
	va_list argList;
	va_start(argList, p_sFormat);
	::StringCchVPrintfA(GetBuffer(1024),1024,p_sFormat,argList);
	va_end(argList);
};

void AStr::StrFormat(char* p_pkcDst,int p_iSize,const char* p_sFormat,...)
{
	va_list argList;
	va_start(argList, p_sFormat);
	::StringCchVPrintfA(p_pkcDst,p_iSize,p_sFormat,argList);
	va_end(argList);
};

/** \brief Build a printf-like formatted string
\param sFormat format string

The maximum length is capped to 1024 bytes.
*/
AStr AStr::Create(const char* p_sFormat,...)
{
	va_list argList;
	va_start(argList, p_sFormat);
	char cB[1024];
	::StringCchVPrintfA(cB,1024,p_sFormat,argList);
	va_end(argList);
	return AStr(cB);
};

/** \brief printf-like formatting from an arglist
\param sFormat format string
\param p_xrL argument list
\code
va_list argList;
va_start(argList, sFormat);
UseString(AStr::CreateArgList(sFormat,argList));
va_end(argList);
\endcode
*/
AStr AStr::CreateArgList(const char* p_sFormat,va_list& p_xrL)
{
	AStr sR;
	::StringCchVPrintfA(sR.GetBuffer(1024),1024,p_sFormat,p_xrL);
	return sR;
};

/** \brief Safe sprintf wrapper
\param iMaxSize maximum size of buffer
\param sBuffer target buffer

*/
void AStr::BufferPrint(int p_iMaxSize,char* p_sBuffer,const char* p_sFormat,...)
{
	va_list xArgList;
	va_start(xArgList, p_sFormat);
	::StringCchVPrintfA(p_sBuffer,p_iMaxSize,p_sFormat,xArgList);
	va_end(xArgList);
};

AStr& AStr::operator+=(const AStr& p_sA)
{
	if(!p_sA.m_sData){return *this;};
	if(!m_sData)
	{
		m_sData=p_sA.m_sData;
		AStrPrivateData::Char2Data(m_sData)->IncRef();
		return *this;
	};
	int iL1=GetSize();
	int iL2=p_sA.GetSize();
	GetBuffer(iL1+iL2+1);
	memcpy(m_sData+iL1,p_sA.m_sData,iL2+1);
	return *this;
};

AStr& AStr::operator+=(const char* p_sA)
{
	if(!m_sData)
	{
		*this=p_sA;
		return *this;
	};
	int iL1=GetSize();
	int iL2=lstrlenA(p_sA);
	GetBuffer(iL1+iL2+1);
	memcpy(m_sData+iL1,p_sA,iL2+1);
	AStrPrivateData::Char2Data(m_sData)->m_iLen=iL1+iL2;
	return *this;
};

AStr& AStr::operator+=(char p_cA)
{
	if(m_sData)
	{
		Isolate();
		AStrPrivateData* pxD=AStrPrivateData::Char2Data(m_sData);
		if((pxD->m_iLen!=-1)&&(pxD->m_iLen+1<pxD->m_iCapacity))
		{
			m_sData[pxD->m_iLen]=p_cA;
			pxD->m_iLen++;
			m_sData[pxD->m_iLen]=0;
			return *this;
		};
	};
	int iL=GetSize();
	GetBuffer(iL+2);
	m_sData[iL]=p_cA;
	m_sData[iL+1]=0;
	AStrPrivateData::Char2Data(m_sData)->m_iLen=iL+1;
	return *this;
};

AStr AStr::operator+(const AStr& p_sA) const
{
	if(!m_sData)
	{
		return p_sA;
	};
	if(!p_sA.m_sData)
	{
		return *this;
	};
	int iL1=GetSize();
	int iL2=p_sA.GetSize();
	AStr sT;
	sT.GetBuffer(iL1+iL2+1);
	memcpy(sT.m_sData,m_sData,iL1);
	memcpy(sT.m_sData+iL1,p_sA.m_sData,iL2+1);
	AStrPrivateData::Char2Data(sT.m_sData)->m_iLen=iL1+iL2;
	return sT;
};

AStr AStr::operator+(const char* p_sA) const
{
	if(!p_sA)
	{
		return *this;
	};
	int iL1=GetSize();
	int iL2=::lstrlenA(p_sA);
	AStr sT;
	sT.GetBuffer(iL1+iL2+1);
	memcpy(sT.m_sData,m_sData,iL1);
	memcpy(sT.m_sData+iL1,p_sA,iL2+1);
	AStrPrivateData::Char2Data(sT.m_sData)->m_iLen=iL1+iL2;
	return sT;
};

AStr AStr::operator+(char p_cA) const
{
	AStr sT(*this);
	int iL=sT.GetSize();
	sT.SetAt(iL,p_cA);
	sT.SetAt(iL+1,0);
	return sT;
};

void AStr::StrNCopy(char* p_sDst,const char* p_sSrc,int p_iMax)
{
	if(p_iMax==0){p_iMax=1<<30;};
	strncpy_s(p_sDst,p_iMax+1,p_sSrc,p_iMax);
	//	strcpy_s(sDst,iMax,sSrc);
};

void AStr::StrCpy(char* p_sDst,const char* p_sSrc,int p_iMax)
{
	if(p_iMax==0){p_iMax=1<<30;};
	::StringCbCopyA(p_sDst,p_iMax,p_sSrc);
};

void AStr::StrCat(char* p_pkcDst,const char* p_sSrc,int p_iMax)
{
	if(p_iMax==0){p_iMax=1<<30;};
	::StringCbCatA(p_pkcDst,p_iMax,p_sSrc);
};

int AStr::StrCmp(const char* p_sS1,const char* p_sS2)
{
	const char* pA=p_sS1;
	const char* pB=p_sS2;
	if(pA==pB){return 0;};
	while((*pA)&&(*pB))
	{
		int iD=(*pA)-(*pB);
		if(iD==0)
		{
			pA++;pB++;
		}
		else if(iD<0)
		{
			return -1;
		}
		else
		{
			return 1;
		};
	};
	if((*pA=='\0')&&(*pB=='\0')){return 0;}
	if(*pA){return 1;};
	return -1;
};

int AStr::StrICmp(const char* p_sS1,const char* p_sS2)
{
	return ::lstrcmpiA(p_sS1,p_sS2);
};

int AStr::StrNCmp(const char* p_sS1,const char* p_sS2,int p_iLen)
{
	const char* pA=p_sS1;
	const char* pB=p_sS2;
	if(pA==pB){return 0;};
	while((*pA)&&(*pB)&&(p_iLen))
	{
		p_iLen--;
		int iD=(*pA)-(*pB);
		if(iD==0)
		{
			pA++;pB++;
		}
		else if(iD<0)
		{
			return -1;
		}
		else
		{
			return 1;
		};
	};
	if(!p_iLen){return 0;};
	if((*pA=='\0')&&(*pB=='\0')){return 0;}
	if(*pA){return 1;};
	return -1;
};

int AStr::StrNICmp(const char* p_sS1,const char* p_sS2,int p_iLen)
{
	int iL1=::lstrlenA(p_sS1);
	int iL2=::lstrlenA(p_sS2);
	iL1=Min(iL1,p_iLen);iL2=Min(iL2,p_iLen);
	return ::CompareStringA(LOCALE_USER_DEFAULT,0,p_sS1,iL1,p_sS2,iL2)-2;
};

const char* AStr::StrChrFind(const char* p_sM,char p_cS)
{
	return ::strchr(p_sM,p_cS);
};

const char* AStr::StrStrFind(const char* p_sM,const char* p_sS)
{
	return ::strstr(p_sM,p_sS);
};

int AStr::Compare(const AStr& p_sOp) const
{
	if((!p_sOp.m_sData)&&(!m_sData)){return 0;}
	else if((p_sOp.m_sData)&&(!m_sData)){if(*p_sOp.m_sData=='\0'){return 0;};return -1;}
	else if((!p_sOp.m_sData)&&(m_sData)){if(*m_sData=='\0'){return 0;};return 1;};
	if(m_sData==p_sOp.m_sData){return 0;};
	return StrCmp(m_sData,p_sOp.m_sData);
	//	return ::CompareStringA(LOCALE_USER_DEFAULT,SORT_STRINGSORT,m_sData,-1,p_sOp.m_sData,-1)-2;
	//	return ::lstrcmpA(m_sData,p_sOp.m_sData);
};

int AStr::Compare(const char* p_sOp) const
{
	if((!p_sOp)&&(!m_sData)){return 0;}
	else if((p_sOp)&&(!m_sData)){if(*p_sOp=='\0'){return 0;};return -1;}
	else if((!p_sOp)&&(m_sData)){if(*m_sData=='\0'){return 0;};return 1;};
	return StrCmp(m_sData,p_sOp);
	//	return ::lstrcmpA(m_sData,p_sOp);
};

int AStr::Compare(const char* p_sOp,int p_iMaxLen) const
{
	if((!p_sOp)&&(!m_sData)){return 0;}
	else if((p_sOp)&&(!m_sData)){if(*p_sOp=='\0'){return 0;};return -1;}
	else if((!p_sOp)&&(m_sData)){if(*m_sData=='\0'){return 0;};return 1;};
	if(m_sData==p_sOp){return 0;};
	return StrNCmp(m_sData,p_sOp,p_iMaxLen);
	//	return ::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData,iL1,p_sOp,iL2)-2;
};

int AStr::CompareMin(const char* p_sOp) const
{
	return Compare(p_sOp,Min(GetSize(),AStr::StrLen(p_sOp)));
};

int AStr::CompareNoCase(const char* p_sOp) const
{
	if((!p_sOp)&&(!m_sData)){return 0;}
	else if((p_sOp)&&(!m_sData)){if(*p_sOp=='\0'){return 0;};return -1;}
	else if((!p_sOp)&&(m_sData)){if(*m_sData=='\0'){return 0;};return 1;};
	return ::lstrcmpiA(m_sData,p_sOp);
};

int AStr::CompareNoCase(const char* p_sOp,int p_iMaxLen) const
{
	if((!p_sOp)&&(!m_sData)){return 0;}
	else if((p_sOp)&&(!m_sData)){if(*p_sOp=='\0'){return 0;};return -1;}
	else if((!p_sOp)&&(m_sData)){if(*m_sData=='\0'){return 0;};return 1;};
	int iL1=GetSize();
	int iL2=::lstrlenA(p_sOp);
	iL1=Min(iL1,p_iMaxLen);iL2=Min(iL2,p_iMaxLen);
	return ::CompareStringA(LOCALE_USER_DEFAULT,NORM_IGNORECASE,m_sData,iL1,p_sOp,iL2)-2;
};

int AStr::CompareNoCaseMin(const char* p_sOp) const
{
	return Compare(p_sOp,Min(GetSize(),AStr::StrLen(p_sOp)));
};

bool AStr::IsLower(char p_cA)
{
	return ::IsCharLowerA(p_cA)==TRUE;
};

bool AStr::IsUpper(char p_cA)
{
	return ::IsCharUpperA(p_cA)==TRUE;
};

bool AStr::IsAlpha(char p_cA)
{
	return ::IsCharAlphaA(p_cA)==TRUE;
};

bool AStr::IsDigit(char p_cA)
{
	return (p_cA>='0')&&(p_cA<='9');
};

bool AStr::IsAlphaNum(char p_cA)
{
	return ::IsCharAlphaNumericA(p_cA)==TRUE;
};

char AStr::ToLower(char p_cA)
{
	char acT[2]={0,0};acT[0]=p_cA;
	::CharLowerA((LPSTR)(acT));
	return acT[0];
};

char AStr::ToUpper(char p_cA)
{
	char acT[2]={0,0};acT[0]=p_cA;
	::CharUpperA((LPSTR)(acT));
	return acT[0];
};

void AStr::MakeUpper()
{
	if(m_sData)
	{
		::CharUpperA(GetBuffer(0));
	};
};

void AStr::MakeLower()
{
	if(m_sData)
	{
		::CharLowerA(GetBuffer(0));
	};
};

AStr AStr::ToUpper() const
{
	if(!m_sData){return AStr();};
	AStr sT(*this);
	sT.MakeUpper();
	return sT;
};

AStr AStr::ToLower() const
{
	if(!m_sData){return AStr();};
	AStr sT(*this);
	sT.MakeLower();
	return sT;
};

AStr AStr::Mid(int p_iStart) const
{
	if(!m_sData){return AStr();};
	return Mid(p_iStart,GetSize());
};

AStr AStr::Mid(int p_iStart,int p_iCount) const
{
	if(!m_sData){return AStr();};
	if(p_iStart<0){p_iCount+=p_iStart;p_iStart=0;};
	int iLen=GetSize()-p_iStart;
	iLen=Min(iLen,p_iCount);
	if(iLen<0)
	{
		return AStr();
	};
	AStr sRet;
	sRet.GetBuffer(iLen+1);
	CopyMemory(sRet.m_sData,m_sData+p_iStart,iLen);
	sRet.m_sData[iLen]=0;
	AStrPrivateData::Char2Data(sRet.m_sData)->m_iLen=iLen;
	return sRet;
};

AStr AStr::Left(int p_iCount) const
{
	return Mid(0,p_iCount);
};

AStr AStr::Right(int p_iCount) const
{
	return Mid(GetSize()-p_iCount,p_iCount);
};

void AStr::Cut(int p_iStart,int p_iCount)
{
	if(!m_sData){return;};
	int iL=GetSize();
	if(p_iStart<0)
	{
		p_iCount+=p_iStart;
		p_iStart=0;
	};
	if(p_iStart+p_iCount>=iL)
	{
		p_iCount=iL-p_iStart;
	};
	if(p_iCount<=0){return;;};
	GetBuffer(0);
	int i;
	for(i=0;i<iL-p_iStart-p_iCount;i++)
	{
		m_sData[i+p_iStart]=m_sData[i+p_iStart+p_iCount];
	};
	m_sData[iL-p_iCount]=0;
};

int AStr::CharFind(const char* p_sString,char p_cA)
{
	int i=0;
	while((p_sString[i]!='\0')&&(p_sString[i]!=p_cA)){i++;};
	if(p_sString[i]!=p_cA){return -1;};
	return i;
};

int AStr::Find(char p_cA) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	for(i=0;i<iL;i++)
	{
		if(m_sData[i]==p_cA){return i;};
	};
	return -1;
};

int AStr::Find(char p_cA,int p_iStart) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	for(i=p_iStart;i<iL;i++)
	{
		if(m_sData[i]==p_cA){return i;};
	};
	return -1;
};

int AStr::Find(const char* p_sA) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	int iSL=lstrlenA(p_sA);
	iL-=iSL;
	for(i=0;i<=iL;i++)
	{
		if(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iSL,p_sA,iSL)==CSTR_EQUAL)
		{
			return i;
		};
	};
	return -1;
};

int AStr::Find(const char* p_sA,int iStart) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	int iSL=lstrlenA(p_sA);
	iL-=iSL;
	for(i=iStart;i<=iL;i++)
	{
		if(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iSL,p_sA,iSL)==CSTR_EQUAL)
		{
			return i;
		};
		//		if(lstrcmp(m_sData+i,sA)==0){return i;};
	};
	return -1;
};

int AStr::Find(const AStr& p_sA) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	int iSL=p_sA.GetSize();
	iL-=iSL;
	for(i=0;i<=iL;i++)
	{
		if(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iSL,p_sA,iSL)==CSTR_EQUAL)
		{
			return i;
		};
		//		if(lstrcmp(m_sData+i,sA.m_sData)==0){return i;};
	};
	return -1;
};

int AStr::Find(const AStr& p_sA,int p_iStart) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	int iSL=p_sA.GetSize();
	iL-=iSL;
	for(i=p_iStart;i<=iL;i++)
	{
		if(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iSL,p_sA,iSL)==CSTR_EQUAL)
		{
			return i;
		};
		//		if(lstrcmp(m_sData+i,sA.m_sData)==0){return i;};
	};
	return -1;
};

int AStr::FindOneOf(const char* p_sCharSet) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	for(i=0;i<iL;i++)
	{
		if(CharFind(p_sCharSet,m_sData[i])!=-1){return i;};
	};
	return -1;
};

int AStr::FindOneOf(const char* p_sCharSet,int p_iStart) const
{
	if(!m_sData){return -1;};
	int i,iL=GetSize();
	for(i=p_iStart;i<iL;i++)
	{
		if(CharFind(p_sCharSet,m_sData[i])!=-1){return i;};
	};
	return -1;
};

int AStr::FindRev(char p_cA) const
{
	int i=GetSize()-1;
	while((i>=0)&&(m_sData[i]!=p_cA)){i--;};
	if(i>=0){return i;};
	return -1;
};

int AStr::FindRev(const char* p_sA) const
{
	if(!p_sA){return -1;};
	int iL=lstrlenA(p_sA);
	int i=GetSize()-iL;
	while((i>=0)&&(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iL,p_sA,iL)==CSTR_EQUAL)){i--;};
	if(i>=0){return i;};
	return -1;
};

void AStr::TrimLeft()
{
	if(!m_sData){return;};
	TrimLeft(" \x09\x0a\x0b\x0c\x0d");
};

void AStr::TrimLeft(char p_cTarget)
{
	if(!m_sData){return;};
	int iL=GetSize();
	int i=0;
	while((i<iL)&&(m_sData[i]==p_cTarget)){i++;};
	Cut(0,i);
};

void AStr::TrimLeft(const char* p_sTargets)
{
	if(!m_sData){return;};
	int i=0,iL=GetSize();
	while((i<iL)&&(CharFind(p_sTargets,m_sData[i])!=-1)){i++;};
	Cut(0,i);
};

void AStr::TrimRight()
{
	if(!m_sData){return;};
	TrimRight(" \x09\x0a\x0b\x0c\x0d");
};

void AStr::TrimRight(char p_cTarget)
{
	if(!m_sData){return;};
	int i=GetSize()-1;
	while((i>=0)&&(m_sData[i]==p_cTarget)){i--;};
	Cut(i+1,GetSize());
};

void AStr::TrimRight(const char* p_sTargets)
{
	if(!m_sData){return;};
	int i=GetSize()-1;
	while((i>=0)&&(CharFind(p_sTargets,m_sData[i])!=-1)){i--;};
	Cut(i+1,GetSize());
};

void AStr::Trim()
{
	if(!m_sData){return;};
	TrimRight();TrimLeft();
};

void AStr::Trim(char p_cTarget)
{
	if(!m_sData){return;};
	TrimRight(p_cTarget);TrimLeft(p_cTarget);
};

void AStr::Trim(const char* p_sTargets)
{
	if(!m_sData){return;};
	TrimRight(p_sTargets);TrimLeft(p_sTargets);
};

void AStr::Fill(char p_cChar,int p_iCount)
{
	char* pxD=GetBuffer(p_iCount+1);
	int i;
	for(i=0;i<p_iCount;i++)
	{
		pxD[i]=p_cChar;
	};
	pxD[i]=0;
};

int AStr::Replace(char p_cOld, char p_cNew)
{
	if(!m_sData){return 0;};
	int iCount=0;
	if(p_cOld!=p_cNew)
	{
		GetBuffer(0);
		char* s=m_sData;
		while(*s)
		{
			if(*s==p_cOld)
			{
				*s=p_cNew;
				iCount++;
			};
			s++;
		};
	};
	return iCount;
};

int AStr::Replace(const char* p_sOld, const char* p_sNew)
{
	if(!m_sData){return 0;};
	int iChanges=0;
	int iPos=0;
	int iSrchLen=(int)lstrlenA(p_sOld);
	int iReplLen=(int)lstrlenA(p_sNew);
	while(
		(iPos=Find(p_sOld,iPos))
		!=-1)
	{
		iChanges++;iPos+=iSrchLen;
	};
	if(iChanges==0){return 0;};
	int iOldLen=GetSize();
	int iNewLen=iOldLen+iChanges*(iReplLen-iSrchLen);
	AStr sOrg=*this;
	GetBuffer(iNewLen+1);
	int iDest=0,iLast;
	iPos=0;iLast=0;
	int iRet=iChanges;
	for(;iChanges>0;iChanges--)
	{
		iLast=iPos;
		iPos=sOrg.Find(p_sOld,iPos);
		if(iLast<iPos)
		{
			memcpy(m_sData+iDest,sOrg.m_sData+iLast,iPos-iLast);
			iDest+=iPos-iLast;
		}
		else
		{
			iPos=iLast;
		};
		iPos+=iSrchLen;
		if(iReplLen>0)
		{
			memcpy(m_sData+iDest,p_sNew,iReplLen);
			iDest+=iReplLen;
		};
	};
	iLast+=iSrchLen;

	memcpy(m_sData+iDest,sOrg.m_sData+iPos,iOldLen-iPos);
	iDest+=iOldLen-iPos;
	m_sData[iDest]=0;
	AStrPrivateData::Char2Data(m_sData)->m_iLen=iDest;
	return iRet;
};

AStr AStr::HTMLEncode(const AStr& p_ksrText)
{
	int iL=p_ksrText.GetSize();
	AStr sRet;
	const char* pcSrc=p_ksrText.c_str();
	int iDstLeft=(iL*5)/4+10;
	char* pcDst=sRet.GetBuffer(iDstLeft);
	int iPos=0;
	while(*pcSrc)
	{
		char cT=*pcSrc++;
		if(cT=='<')
		{
			if(iPos+5>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]='&';
			pcDst[iPos++]='l';
			pcDst[iPos++]='t';
			pcDst[iPos++]=';';
		}
		else if(cT=='>')
		{
			if(iPos+5>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]='&';
			pcDst[iPos++]='g';
			pcDst[iPos++]='t';
			pcDst[iPos++]=';';
		}
		else if(cT=='&')
		{
			if(iPos+6>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]='&';
			pcDst[iPos++]='a';
			pcDst[iPos++]='m';
			pcDst[iPos++]='p';
			pcDst[iPos++]=';';
		}
		else if(cT=='\"')
		{
			if(iPos+7>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]='&';
			pcDst[iPos++]='q';
			pcDst[iPos++]='u';
			pcDst[iPos++]='o';
			pcDst[iPos++]='t';
			pcDst[iPos++]=';';
		}
		else if(cT=='\'')
		{
			if(iPos+8>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]='&';
			pcDst[iPos++]='l';
			pcDst[iPos++]='s';
			pcDst[iPos++]='q';
			pcDst[iPos++]='u';
			pcDst[iPos++]='o';
			pcDst[iPos++]=';';
		}
		else
		{
			if(iPos+1>=iDstLeft){iDstLeft+=64;pcDst=sRet.GetBuffer(iDstLeft);};
			pcDst[iPos++]=cT;
		};
	};
	pcDst[iPos++]=0;
	return sRet;
};


AStr AStr::CStyleEncode(const AStr& p_sT,bool p_bEscapeSpaces,bool p_bConvertSpecialChars)
{
	AStr sR;
	int i,iL=p_sT.GetSize();
	for(i=0;i<iL;i++)
	{
		unsigned char c=(unsigned char)p_sT.GetAt(i);
		if(c=='\"')
		{
			sR+="\\\"";
		}
		else if(c=='\'')
		{
			sR+="\\\'";
		}
		else if(c=='\\')
		{
			sR+="\\\\";
		}
		else if((c&0x7f)>32)
		{
			sR+=c;
		}
		else
		{
			if(c==' ')
			{
				if(p_bEscapeSpaces)
				{
					sR+="\\_";
				}
				else
				{
					sR+=" ";
				};
			}
			else if(c=='\n')
			{
				sR+="\\n";
			}
			else if(c=='\t')
			{
				sR+="\\t";
			}
			else if(c=='\r')
			{
				sR+="\\r";
			}
			else if(c=='\'')
			{
				sR+="\\'";
			}
			else if(c=='\"')
			{
				sR+="\\\"";
			}
			else
			{
				if(p_bConvertSpecialChars)
				{
					int iV=(int)c;
					sR+=AStr::Create("\\x%02x",iV);
				}
				else {sR+=c;};
			};
		};
	};

	return sR;
};

AStr AStr::CStyleDecode(const AStr& p_sT)
{
	AStr sR;
	int i,iL=p_sT.GetSize();
	for(i=0;i<iL;i++)
	{
		char c=p_sT.GetAt(i);
		if(c!='\\')
		{
			sR+=c;
		}
		else
		{
			i++;
			if(i<iL)
			{
				c=p_sT.GetAt(i);
				if(c=='n')
				{
					sR+="\n";
				}
				else if(c=='t')
				{
					sR+="\t";
				}
				else if(c=='r')
				{
					sR+="\r";
				}
				else if(c=='x')
				{
					i++;
					if(i<iL-1)
					{
						char ac[3];
						ac[0]=p_sT.GetAt(i);
						ac[1]=p_sT.GetAt(i+1);
						ac[2]=0;
						i++;
						char* sT2;
						int iVal=strtol(ac,&sT2,16);
						sR+=((char)iVal);
					};
				}
				else if(c=='_')
				{
					sR+=" ";
				}
				else
				{
					sR+=c;
				}
			};
		};
	};
	return sR;
};

int AStr::FindCStyle(char p_cA,int p_iStart) const
{
	bool bEscape=false;
	bool bInString1=false;
	bool bInString2=false;
	int i,iLen=GetSize();
	for(i=p_iStart;i<iLen;i++)
	{
		char c=GetAt(i);
		if((c=='\\')&&(!bEscape))
		{
			bEscape=true;
		}
		else
		{
			if(bInString1)
			{
				if((c=='\"')&&(!bEscape))
				{
					bInString1=false;
				}
				else
				{
					if(bEscape){bEscape=false;};
				};
			}
			else if(bInString2)
			{
				if((c=='\'')&&(!bEscape))
				{
					bInString2=false;
				}
				else
				{
					if(bEscape){bEscape=false;};
				};
			}
			else if(c=='\"')
			{
				bInString1=true;
			}
			else if(c=='\'')
			{
				bInString2=true;
			}
			else
			{
				if(bEscape)
				{
					bEscape=false;
				}
				else
				{
					if(c==p_cA)
					{
						return i;
					};
				};
			};
		};
	};
	return -1;
};

int AStr::FindCStyle(const char* p_sA,int p_iStart) const
{
	bool bEscape=false;
	bool bInString1=false;
	bool bInString2=false;
	int iSearchLen=StrLen(p_sA);
	int i,iLen=GetSize();
	for(i=p_iStart;i<iLen;i++)
	{
		char c=GetAt(i);
		if((c=='\\')&&(!bEscape))
		{
			bEscape=true;
		}
		else
		{
			if(bInString1)
			{
				if((c=='\"')&&(!bEscape))
				{
					bInString1=false;
				}
				else
				{
					if(bEscape){bEscape=false;};
				};
			}
			else if(bInString2)
			{
				if((c=='\'')&&(!bEscape))
				{
					bInString2=false;
				}
				else
				{
					if(bEscape){bEscape=false;};
				};
			}
			else if(c=='\"')
			{
				bInString1=true;
			}
			else if(c=='\'')
			{
				bInString2=true;
			}
			else
			{
				if(bEscape)
				{
					bEscape=false;
				}
				else
				{
					if(iSearchLen>iLen-1){return -1;};
					if(::CompareStringA(LOCALE_USER_DEFAULT,0,m_sData+i,iSearchLen,p_sA,iSearchLen)==CSTR_EQUAL)
					{
						return i;
					};
				};
			};
		};
	};
	return -1;
};

void AStr::SplitDelim(DArray<AStr> &p_asTokens,const char* p_sSplitChars) const
{
	p_asTokens=0;
	AStr sSplitchars(p_sSplitChars);
	if(sSplitchars.GetSize()==0)
	{
		for(int i=0;i<GetSize();i++)
		{
			p_asTokens.Add(AStr(GetAt(i)));
		};
		return;
	};
	AStr sOld(*this);
	AStr sCur;
	for(int i=0;i<GetSize();i++)
	{
		char c=GetAt(i);
		int iF=sSplitchars.Find(c);
		if(iF!=-1)
		{
			if(sCur.GetSize()>0){p_asTokens.Add(sCur);sCur.Clear();};
			p_asTokens.Add(AStr(c));
		}
		else
		{
			sCur+=c;
		};
	};
	if(sCur.GetSize()) {p_asTokens.Add(sCur);};
};

void AStr::Split(DArray<AStr> &p_asTokens,const char* p_sSplitChars,bool p_bIgnoreEmptyTokens) const
{
	p_asTokens=0;
	AStr sSplitchars(p_sSplitChars);
	if(sSplitchars.GetSize()==0)
	{
		for(int i=0;i<GetSize();i++)
		{
			p_asTokens.Add(AStr(GetAt(i)));
		};
		return;
	};
	AStr sOld(*this);
	for(int i=0;i<sOld.GetSize();i++)
	{
		char c=sOld.GetAt(i);
		int iF=sSplitchars.Find(c);
		if(iF!=-1)
		{
			AStr sSplit=sOld.Left(i);
			sOld.Cut(0,i+1);
			if((i>0)||(!p_bIgnoreEmptyTokens)) {
				p_asTokens.Add(sSplit);
			};
			i=-1;
		};
	};
	if(sOld.GetSize()) {p_asTokens.Add(sOld);};
};

void AStr::SplitCStyle(DArray<AStr> &p_asTokens,const char* p_sSplitChars,bool p_bIgnoreEmptyTokens) const
{
	p_asTokens=0;
	AStr sSplitchars(p_sSplitChars);
	if(sSplitchars.GetSize()==0)
	{
		for(int i=0;i<GetSize();i++)
		{
			p_asTokens.Add(AStr(GetAt(i)));
		};
		return;
	};
	bool bEscape=false;
	bool bInString1=false;
	bool bInString2=false;
	int i,iLen=GetSize();
	AStr sTok;
	for(i=0;i<iLen;i++)
	{
		char c=GetAt(i);
		if((c=='\\')&&(!bEscape))
		{
			bEscape=true;
		}
		else
		{
			if(bInString1)
			{
				if((c=='\"')&&(!bEscape))
				{
					bInString1=false;
					sTok+='\"';
				}
				else
				{
					if(bEscape){sTok+="\\";bEscape=false;};
					sTok+=c;
				};
			}
			else if(bInString2)
			{
				if((c=='\'')&&(!bEscape))
				{
					bInString2=false;
					sTok+='\'';
				}
				else
				{
					if(bEscape){sTok+="\\";bEscape=false;};
					sTok+=c;
				};
			}
			else if(c=='\"')
			{
				bInString1=true;sTok+='\"';
			}
			else if(c=='\'')
			{
				bInString2=true;sTok+='\'';
			}
			else
			{
				if(bEscape)
				{
					sTok+="\\";
					sTok+=c;
					bEscape=false;
				}
				else
				{
					if(sSplitchars.Find(c)!=-1)
					{
						if((sTok.GetSize()>0)||(!p_bIgnoreEmptyTokens))
						{
							p_asTokens.Add(sTok);
						};
						sTok.Clear();
					}
					else
					{
						sTok+=c;
					};
				};
			};
		};
	};
	if(sTok.GetSize()) {p_asTokens.Add(sTok);};
};

AStr AStr::Join(const DArray<AStr>& p_kasL,const char* p_pkcDelim,int p_iStartElem,int p_iEndElem)
{
	AStr sT;
	int i,iC=p_kasL.GetSize();
	if(p_iEndElem==-1){p_iEndElem=iC-1;};
	p_iStartElem=Clamp(p_iStartElem,0,iC-1);
	p_iEndElem=Clamp(p_iEndElem,p_iStartElem,iC-1);
	for(i=p_iStartElem;i<=p_iEndElem;i++)
	{
		sT+=p_kasL.GetAtConst(i);
		if((p_pkcDelim)&&(i<p_iEndElem)){sT+=p_pkcDelim;};
	};
	return sT;
};

bool AStr::WildCardMatch(const char* p_sText,const char* p_sPattern)
{
	const char *pcCP=NULL;
	const char *pcMP=NULL;

	while((*p_sText)&&(*p_sPattern!='*'))
	{
		if((*p_sPattern!=*p_sText)&&(*p_sPattern!='?'))
		{
			return false;
		}
		p_sPattern++;
		p_sText++;
	}

	while(*p_sText)
	{
		if(*p_sPattern=='*')
		{
			if(!*++p_sPattern)
			{
				return true;
			};
			pcMP=p_sPattern;
			pcCP=p_sText+1;
		}
		else if((*p_sPattern==*p_sText)||(*p_sPattern=='?'))
		{
			p_sPattern++;
			p_sText++;
		}
		else
		{
			p_sPattern=pcMP;
			p_sText=pcCP++;
		};
	};
	return (!*p_sPattern);
};

bool AStr::WildCardMatch(const char* p_sText,const char* p_sPattern,DArray<AStr>& po_asrMatches)
{
	po_asrMatches=0;
	const char *pcCP=NULL;
	const char *pcMP=NULL;
	AStr sCur;
	while((*p_sText)&&(*p_sPattern!='*'))
	{
		if(*p_sPattern!=*p_sText)
		{
			if(*p_sPattern!='?')
			{
				return false;
			};
			po_asrMatches.Add(AStr(*p_sText));
		}
		p_sPattern++;
		p_sText++;
	}

	while(*p_sText)
	{
		if(*p_sPattern=='*')
		{
			if(sCur.GetSize()>0){po_asrMatches.Add(sCur);sCur.Clear();};
			if(!*++p_sPattern)
			{
				return true;
			};
			pcMP=p_sPattern;
			pcCP=p_sText+1;
		}
		else if((*p_sPattern==*p_sText)||(*p_sPattern=='?'))
		{
			if(sCur.GetSize()>0){po_asrMatches.Add(sCur);sCur.Clear();};
			if(*p_sPattern!=*p_sText)
			{
				po_asrMatches.Add(AStr(*p_sText));
			};
			p_sPattern++;
			p_sText++;
		}
		else
		{
			sCur+=*p_sText;
			p_sPattern=pcMP;
			p_sText=pcCP++;
		};
	};
	if(sCur.GetSize()>0){po_asrMatches.Add(sCur);sCur.Clear();};
	return (!*p_sPattern);
};

AStr AStr::WildCardRewrite(const char* p_sText,const char* p_sPattern,const char* p_sRewrite)
{
	const char *pcCP=NULL;
	const char *pcMP=NULL;
	DArray<AStr> asOut;
	AStr sCur;
	while((*p_sText)&&(*p_sPattern!='*'))
	{
		if(*p_sPattern!=*p_sText)
		{
			if(*p_sPattern!='?')
			{
				return "";
			};
			asOut.Add(AStr(*p_sText));
		}
		p_sPattern++;
		p_sText++;
	}

	while(*p_sText)
	{
		if(*p_sPattern=='*')
		{
			if(sCur.GetSize()>0){asOut.Add(sCur);sCur.Clear();};
			if(!*++p_sPattern)
			{
				return AStr(p_sText);
			};
			pcMP=p_sPattern;
			pcCP=p_sText+1;
		}
		else if((*p_sPattern==*p_sText)||(*p_sPattern=='?'))
		{
			if(sCur.GetSize()>0){asOut.Add(sCur);sCur.Clear();};
			if(*p_sPattern!=*p_sText)
			{
				asOut.Add(AStr(*p_sText));
			};
			p_sPattern++;
			p_sText++;
		}
		else
		{
			sCur+=*p_sText;
			p_sPattern=pcMP;
			p_sText=pcCP++;
		};
	};
	if(sCur.GetSize()>0){asOut.Add(sCur);sCur.Clear();};
	if(*p_sPattern){return "";};
	AStr sR;
	while(*p_sRewrite)
	{
		char c=*(p_sRewrite++);
		if(c!='%')
		{
			sR+=c;
		}
		else
		{
			if(*p_sRewrite=='%')
			{
				sR+='%';
				p_sRewrite;
			}
			else
			{
				int iIdx=(*p_sRewrite)-'1';
				if((iIdx>=0)&&(iIdx<asOut.GetSize()))
				{
					sR+=asOut[iIdx];
				}
				else
				{
					sR+=*p_sRewrite;
				};
				p_sRewrite++;
			};
		};
	};
	return sR;
};

AStr AStr::Repeat(int p_iNum) const
{
	if(p_iNum<=0){return AStr();};
	if(p_iNum==1){return *this;};
	AStr sRet;
	int iL=GetSize();
	char* pcB=sRet.GetBuffer(iL*p_iNum+1);
	const char* pcO=c_str();
	while(p_iNum>0)
	{
		memcpy(pcB,pcO,iL);
		pcB+=iL;
		p_iNum--;
	};
	*pcB=0;
	return sRet;
};

